package com.nuc.seckill.controller;

import com.nuc.seckill.base.*;
import com.nuc.seckill.entity.CommonWebUser;
import com.nuc.seckill.exception.ErrorMessage;
import com.nuc.seckill.rocketmq.util.MqSender;
import com.nuc.seckill.service.SeckillService;
import com.nuc.seckill.utils.WebUserUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Objects;
import java.util.concurrent.*;

/**
 * 秒杀接口
 *
 * @author 石一歌
 * @date 2022-08-09 23:57
 */
@RestController
@RequestMapping(value = "/seckill")
@Slf4j
public class SeckillController {
    private static final int DELAY_MILLSECONDS = 1000;
    /**
     * 延时双删线程池
     *
     * @date 2022/8/20 10:39
     */
    private static final ExecutorService CACHED_THREAD_POOL = new ThreadPoolExecutor(0,
            Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
    @Resource
    private SeckillService seckillService;
    @Resource
    private MqSender mqSender;

    /**
     * 原始逻辑下单
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/9 23:58
     */
    @RequestMapping(value = "/simple/order")
    public BaseResponse<?> simpleOrder(@Valid @RequestBody BaseRequest<SeckillReq> request) {
        // 验证token，获取user信息
        CommonWebUser user = WebUserUtil.getLoginUser();
        if (Objects.isNull(user)) {
            return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
        }
        SeckillReq req = request.getData();
        req.setUserId(user.getId());
        return seckillService.sOrder(req);
    }

    /**
     * synchronized锁
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/9 23:58
     */
    @RequestMapping(value = "/synchronized/order")
    public synchronized BaseResponse<?> synchronizedOrder(@Valid @RequestBody BaseRequest<SeckillReq> request) {
        // 验证token，获取user信息
        CommonWebUser user = WebUserUtil.getLoginUser();
        if (Objects.isNull(user)) {
            return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
        }
        SeckillReq req = request.getData();
        req.setUserId(user.getId());
        return seckillService.sOrder(req);
    }

    /**
     * 悲观锁
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/9 23:58
     */
    @RequestMapping(value = "/pessimistic/order")
    public BaseResponse<?> pessimisticOrder(@Valid @RequestBody BaseRequest<SeckillReq> request) {
        // 验证token，获取user信息
        CommonWebUser user = WebUserUtil.getLoginUser();
        if (Objects.isNull(user)) {
            return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
        }
        SeckillReq req = request.getData();
        req.setUserId(user.getId());
        return seckillService.pOrder(req);
    }

    /**
     * 乐观锁
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/10 21:58
     */
    @RequestMapping(value = "/optimistic/order")
    public BaseResponse<?> optimisticOrder(@Valid @RequestBody BaseRequest<SeckillReq> request) {
        try {
            CommonWebUser user = WebUserUtil.getLoginUser();
            if (Objects.isNull(user)) {
                return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
            }
            SeckillReq req = request.getData();
            req.setUserId(user.getId());
            return seckillService.oOrder(req);
        } catch (Exception e) {
            log.error("===[秒杀异常！]===", e);
        }
        return BaseResponse.error(ErrorMessage.SYS_ERROR);
    }

    /**
     * redis缓存
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse
     * @author 石一歌
     * @date 2022/8/11 15:50
     */
    @RequestMapping(value = "/cache/order")
    public BaseResponse<?> cacheOrder(@Valid @RequestBody BaseRequest<SeckillReq> request) {
        try {
            CommonWebUser user = WebUserUtil.getLoginUser();
            if (Objects.isNull(user)) {
                return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
            }
            SeckillReq req = request.getData();
            req.setUserId(user.getId());
            return seckillService.cOrder(req);
        } catch (Exception e) {
            log.error("===[秒杀异常！]===", e);
        }
        return BaseResponse.error(ErrorMessage.SYS_ERROR);
    }

    /**
     * redission
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse
     * @author 石一歌
     * @date 2022/8/11 22:42
     */
    @RequestMapping(value = "/redission/order")
    public BaseResponse<?> redissionOrder(@Valid @RequestBody BaseRequest<SeckillReq> request) {
        CommonWebUser user = WebUserUtil.getLoginUser();
        if (Objects.isNull(user)) {
            return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
        }
        SeckillReq req = request.getData();
        req.setUserId(user.getId());
        return seckillService.rOrder(req);
    }

    /**
     * nginx+lua
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse
     * @author 石一歌
     * @date 2022/8/16 22:36
     */
    @RequestMapping(value = "/nginx/order")
    public BaseResponse<?> nginxOrder(@Valid @RequestBody BaseRequest<SeckillReq> request) {
        try {
            CommonWebUser user = WebUserUtil.getLoginUser();
            if (Objects.isNull(user)) {
                return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
            }
            SeckillReq req = request.getData();
            req.setUserId(user.getId());
            return seckillService.nOrder(req);
        } catch (Exception e) {
            log.error("===秒杀发生异常！===", e);
        }
        return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
    }

    /**
     * 获取验证值接口
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<java.lang.String>
     * @author 石一歌
     * @date 2022/8/13 22:31
     */
    @RequestMapping(value = "/verifyHash")
    public BaseResponse<?> getVerifyHash(@RequestBody @Valid BaseRequest<SeckillReq> request) {
        CommonWebUser user = WebUserUtil.getLoginUser();
        if (Objects.isNull(user)) {
            return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
        }
        SeckillReq req = request.getData();
        req.setUserId(user.getId());
        return seckillService.getVerifyHash(req);
    }

    /**
     * 下单v1 服务限流
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/14 21:33
     */
    @RequestMapping(value = "/v1/order")
    public BaseResponse<?> orderV1(@Valid @RequestBody BaseRequest<SeckillReq> request) {
        try {
            CommonWebUser user = WebUserUtil.getLoginUser();
            if (Objects.isNull(user)) {
                return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
            }
            SeckillReq req = request.getData();
            req.setUserId(user.getId());
            return seckillService.orderV1(req);
        } catch (Exception e) {
            log.error("===秒杀发生异常！===", e);
        }
        return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
    }

    /**
     * 下单v2 服务限流+验证码
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/14 21:33
     */
    @RequestMapping(value = "/v2/order")
    public BaseResponse<?> orderV2(@Valid @RequestBody BaseRequest<SeckillReqV2> request) {
        try {
            CommonWebUser user = WebUserUtil.getLoginUser();
            if (Objects.isNull(user)) {
                return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
            }
            SeckillReqV2 req = request.getData();
            req.setUserId(user.getId());
            return seckillService.orderV2(req);
        } catch (Exception e) {
            log.error("===秒杀发生异常！===", e);
        }
        return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
    }

    /**
     * 下单v3 服务限流+图片验证码
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/15 15:36
     */
    @RequestMapping(value = "/v3/order")
    public BaseResponse<?> orderV3(@Valid @RequestBody BaseRequest<SeckillReqV3> request) {
        try {
            CommonWebUser user = WebUserUtil.getLoginUser();
            if (Objects.isNull(user)) {
                return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
            }
            SeckillReqV3 req = request.getData();
            req.setUserId(user.getId());
            return seckillService.orderV3(req);
        } catch (Exception e) {
            log.error("===秒杀发生异常！===", e);
        }
        return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
    }

    /**
     * 下单v4 服务限流+验证码+用户频次限制
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/17 0:11
     */
    @RequestMapping(value = "/v4/order")
    public BaseResponse<?> orderV4(@Valid @RequestBody BaseRequest<SeckillReqV2> request) {
        try {
            CommonWebUser user = WebUserUtil.getLoginUser();
            if (Objects.isNull(user)) {
                return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
            }
            SeckillReqV2 req = request.getData();
            req.setUserId(user.getId());
            return seckillService.orderV4(req);
        } catch (Exception e) {
            log.error("===秒杀发生异常！===", e);
        }
        return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
    }

    /**
     * 下单v5 先更新数据库，在再删缓存
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/20 10:25
     */
    @RequestMapping(value = "/v5/order")
    public BaseResponse<?> orderV5(@Valid @RequestBody BaseRequest<SeckillReqV2> request) {
        try {
            CommonWebUser user = WebUserUtil.getLoginUser();
            if (Objects.isNull(user)) {
                return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
            }
            SeckillReqV2 req = request.getData();
            req.setUserId(user.getId());
            return seckillService.orderV5(req);
        } catch (Exception e) {
            log.error("===秒杀发生异常！===", e);
        }
        return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
    }

    /**
     * 下单v6 延时双删
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/20 10:42
     */
    @RequestMapping(value = "/v6/order")
    public BaseResponse<?> orderV6(@Valid @RequestBody BaseRequest<SeckillReqV2> request) {
        SeckillReqV2 req = request.getData();
        try {
            CommonWebUser user = WebUserUtil.getLoginUser();
            if (Objects.isNull(user)) {
                return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
            }
            req.setUserId(user.getId());
            return seckillService.orderV6(req);
        } catch (Exception e) {
            log.error("===秒杀发生异常！===", e);
            return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
        } finally {
            // 延时指定时间后再次删除缓存
            CACHED_THREAD_POOL.execute(new DeleteCacheByThread(req.getProductId()));
        }
    }

    /**
     * 下单v7 删除缓存重试机制
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/23 23:28
     */
    @RequestMapping(value = "/v7/order")
    public BaseResponse<?> orderV7(@Valid @RequestBody BaseRequest<SeckillReqV2> request) {
        SeckillReqV2 req = request.getData();
        try {
            CommonWebUser user = WebUserUtil.getLoginUser();
            if (Objects.isNull(user)) {
                return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
            }
            req.setUserId(user.getId());
            return seckillService.orderV5(req);
        } catch (Exception e) {
            log.error("===秒杀发生异常！===", e);
            return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
        } finally {
            // 消息队列处理 “缓存删除重试” 逻辑
            mqSender.sendDelCacheMsg(new SendMsgReq.DelCacheReq().setProductId(req.getProductId()));
        }
    }

    /**
     * 下单v8 订单异步处理优化
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/9/3 17:40
     */
    @RequestMapping(value = "/v8/order")
    public BaseResponse<?> orderV8(@Valid @RequestBody BaseRequest<SeckillReqV2> request) {
        SeckillReqV2 req = request.getData();
        try {
            CommonWebUser user = WebUserUtil.getLoginUser();
            //校验用户
            if (Objects.isNull(user)) {
                return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
            }
            req.setUserId(user.getId());
            //下单
            BaseResponse<?> res = seckillService.orderV8(req);
            //下单成功，生产消息
            if (res.getCode() != 0) {
                mqSender.sendOrderMsgByAsync(new SendMsgReq.OrderReq().setProductId(req.getProductId()).setUserId(user.getId()));
            }
            return BaseResponse.OK;
        } catch (Exception e) {
            log.error("===秒杀发生异常！===", e);
            return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
        }
    }

    /**
     * nginx+lua通过后，调用预下单方法，交给MQ，待订单系统消费消息，完成订单创建
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/9/3 17:54
     */
    @RequestMapping(value = "/prepare/order")
    public BaseResponse<?> prepareOrder(@Valid @RequestBody BaseRequest<SeckillReq> request) {
        SeckillReq req = request.getData();
        try {
            CommonWebUser user = WebUserUtil.getLoginUser();
            if (Objects.isNull(user)) {
                return BaseResponse.error(ErrorMessage.LOGIN_ERROR);
            }
            req.setUserId(user.getId());
            mqSender.sendOrderMsgByAsync(new SendMsgReq.OrderReq().setProductId(req.getProductId()).setUserId(user.getId()));
        } catch (Exception e) {
            log.error("===秒杀发生异常！===", e);
            return BaseResponse.error(ErrorMessage.SECKILL_FAILED);
        }
        return BaseResponse.OK;
    }

    /**
     * 检查缓存中用户是否已经生成订单
     *
     * @param req 请求
     * @return com.nuc.seckill.base.BaseResponse<java.lang.String>
     * @author 石一歌
     * @date 2022/9/3 18:23
     */
    @RequestMapping(value = "/final/orderQuery")
    public BaseResponse<?> orderQuery(@Valid @RequestBody SeckillReq req) {
        // 检查缓存中该用户是否已经下单过
        Boolean checkRepeat = seckillService.hasOrderedUserCache(req.getProductId(), req.getUserId());
        if (checkRepeat == null) {
            log.error("===[验证缓存中用户是否已经生成订单时异常！]===");
            return BaseResponse.error(ErrorMessage.SYS_ERROR);
        } else if (checkRepeat) {
            log.error("===[用户{}已经生成订单！]===", req.getUserId());
            return BaseResponse.ok("恭喜您，已经抢购成功！");
        }
        return BaseResponse.ok("很抱歉，你的订单尚未生成，请继续等待结果！");
    }

    /**
     * 数据源库存
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/20 10:19
     */
    @RequestMapping(value = "/datasource/stock")
    public BaseResponse<?> getStockByDataSource(@Valid @RequestBody BaseRequest<SeckillReq> request) {
        try {
            SeckillReq req = request.getData();
            return BaseResponse.ok(seckillService.getStockByDataSource(req.getProductId()));
        } catch (Exception e) {
            return BaseResponse.error(ErrorMessage.SYS_ERROR);
        }
    }

    /**
     * 缓存库存
     *
     * @param request 请求
     * @return com.nuc.seckill.base.BaseResponse<?>
     * @author 石一歌
     * @date 2022/8/20 10:20
     */
    @RequestMapping(value = "/cache/stock")
    public BaseResponse<?> getStockByCache(@RequestBody BaseRequest<SeckillReq> request) {
        try {
            SeckillReq req = request.getData();
            return BaseResponse.ok(seckillService.getStockByCache(req.getProductId()));
        } catch (Exception e) {
            return BaseResponse.error(ErrorMessage.SYS_ERROR);
        }
    }

    /**
     * 缓存再删除线程
     */
    private class DeleteCacheByThread implements Runnable {
        private final Long sid;

        public DeleteCacheByThread(Long sid) {
            this.sid = sid;
        }

        @Override
        public void run() {
            try {
                log.info("异步执行缓存再删除，商品id：[{}]， 首先休眠：[{}] 毫秒", sid, DELAY_MILLSECONDS);
                Thread.sleep(DELAY_MILLSECONDS);
                seckillService.delStockByCache(sid);
                log.info("再次删除商品id：[{}] 缓存", sid);
            } catch (Exception e) {
                log.error("delCacheByThread执行出错", e);
            }
        }
    }
}
